﻿using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Threading;

using VSTalk.Engine.Core.Context;
using VSTalk.Engine.Core.XmppConnection;
using VSTalk.Model;
using VSTalk.Tools;

using agsXMPP;
using agsXMPP.protocol.client;

namespace VSTalk.Engine.Core.EntitySession
{
    public class RoomContext : IRoomContext
    {
        private readonly XmppClientConnection _connection;
        private bool _isActive;
        private Client _client;
        private ISubscription _clientContextUnit;
        private ISubscription _selfUnit;
        private IActionWrapper<object, Presence> _onPresence;

        private IClientContext ClientContext
        {
            get { return AppServices.Get<ISessionModel>().GetClientContext(_client); }
        }

        public Jid Bare { get; private set; }

        public bool IsActive
        {
            get { return _isActive; }
            set
            {
                _isActive = value;
                PropertyChanged.Notify(() => IsActive);
            }
        }

        public Room Room { get; set; }

        public ObservableCollection<IRoomMember> Members { get; private set; }

        public RoomContext(Room room)
        {
            Bare = new Jid(room.Name, room.Server, null);
            Room = room;
            Members = new ObservableCollection<IRoomMember>();

            _client = AppServices.Get<IModelContext>().GetClientByRoom(room);
            _connection = AppServices.Get<IConnectionManager>()
                    .GetOrCreateConnection(_client)
                    .Connection;
        }

        public void StartListen()
        {
            Subscribe();
            StartHandling();
        }

        public void StopListen()
        {
            StopHandling();
            Unsubscribe();
        }

        private void Subscribe()
        {
            var dispatcher = Dispatcher.CurrentDispatcher;

            _onPresence = dispatcher.Wrap<object, Presence>(OnPresence);
            _connection.OnPresence += _onPresence.Exec;
        }

        private void StartHandling()
        {
            _clientContextUnit = ClientContext.SubscribeToChange(() => ClientContext.State, sender =>
            {
                if (ClientContext.State == ClientState.Disconnected)
                {
                    IsActive = false;
                }
            });
            _selfUnit = this.SubscribeToChange(() => IsActive, sender => !IsActive, sender =>
            {
                foreach (var roomMember in Members.ToList())
                {
                    Members.Remove(roomMember);
                }
            });
        }

        private void StopHandling()
        {
            _clientContextUnit.Unsubscribe();
            _selfUnit.Unsubscribe();
        }

        private void Unsubscribe()
        {
            _connection.OnPresence -= _onPresence.Exec;
        }

        private void OnPresence(object sender, Presence p)
        {
            if (!string.Equals(p.From.Bare, Bare)) return;
            var member = GetOrCreateMember(p.From);
            member.Name = p.From.Resource;
            if (p.Type == PresenceType.unavailable)
            {
                Members.Remove(member);
            }
        }

        private IRoomMember GetOrCreateMember(Jid @from)
        {
            return Members.FirstOrDefault(member => Equals(member.Jid, @from)) ??
                   CreateMember(from);
        }

        private IRoomMember CreateMember(Jid jid)
        {
            var member = new RoomMember
            {
                Jid = jid
            };
            Members.Add(member);
            return member;
        }

        public event PropertyChangedEventHandler PropertyChanged;

    }
}